using System;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Threading;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.event
{


// TODO: rewrite this one, use new concurrent util stuff

	internal class EventQueue : Thread
	{
		private static Logger log = Logger.getLogger(typeof(EventQueue).Name);

		private readonly LinkedList<EventElement> queueList = new LinkedList<EventElement>();
	// time to wait before next flush
		private int waitTime = 50;
	// how many new elements before notify for flush
		private int notifyOnCount = 100;
		private volatile bool run = true;
		private volatile bool destroyed = false;
		private int elementCount = 0;

		private readonly EventManager eventManager;

		internal EventQueue(EventManager eventManager) : base("EventQueueConsumer")
		{
			this.eventManager = eventManager;
		}

		private void queue(EventElement eventElement)
		{
			queueList.AddLast(eventElement);
			elementCount++;

			if (elementCount > notifyOnCount)
			{
				elementCount = 0;
				this.notify();
			}
		}

		private int flushAll()
		{
			int numElements = 0;
			while (queueList.Count > 0)
			{
				sendEvent(queueList.RemoveFirst());
				numElements++;
			}
			elementCount = 0;
			return numElements;
		}

		private void sendEvent(EventElement eventElement)
		{
			eventManager.sendReActiveEvent(eventElement.getEvent(), eventElement.getEventData());
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void run()
		{
			try
			{
				EventElement eventElement = eventManager.getNextEventElement();
				while (eventElement != null)
				{
					queue(eventElement);
					eventElement = eventManager.getNextEventElement();
				}

				while (run || queueList.Count > 0)
				{
					flushAll();
					try
					{
					// if count low increase sleep time?
					// if count high decrease sleep time?
						wait(waitTime);
					}
					catch (InterruptedException e)
					{
						Thread.interrupted();
					}

					eventElement = eventManager.getNextEventElement();
					while (eventElement != null)
					{
						this.queue(eventElement);
						eventElement = eventManager.getNextEventElement();
					}
				}
				eventElement = eventManager.getNextEventElement();
				while (eventElement != null)
				{
					queueList.AddLast(eventElement);
					eventElement = eventManager.getNextEventElement();
				}
			}
			catch (System.Exception t)
			{
				t.printStackTrace();
				log.severe("Event consumer queue caught thowable, " + "queue destroyed");
			}
			destroyed = true;
		}

		internal virtual void shutdown()
		{
			run = false;
		}

		internal virtual void waitForDestroy()
		{
			while (!destroyed)
			{
				try
				{
					Thread.Sleep(waitTime);
				}
				catch (InterruptedException e)
				{
					Thread.interrupted();
					Console.WriteLine("Error " + e);
				}
			}
		}

		internal virtual void setWaitTime(int time)
		{
			waitTime = time;
		}

		internal virtual int getWaitTime()
		{
			return waitTime;
		}

		internal virtual void setNotifyOnCount(int count)
		{
			notifyOnCount = count;
		}

		internal virtual int getNotifyOnCount()
		{
			return notifyOnCount;
		}
	}
}